package com.i2863.shiroDemo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.i2863.shiroDemo.config.CacheConstant;
import com.i2863.shiroDemo.dto.MenuInfo;
import com.i2863.shiroDemo.dto.PermissionInfo;
import com.i2863.shiroDemo.entity.Permission;
import com.i2863.shiroDemo.entity.Role;
import com.i2863.shiroDemo.mapper.PermissionMapper;
import com.i2863.shiroDemo.service.IPermissionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements IPermissionService {

    @Cacheable(value = CacheConstant.PERMISSION_KEY_PREFIX, key = "#root.method.name", unless = "#result == null")
    @Override
    public List<PermissionInfo> allPermissionInfo() {
        return this.baseMapper.allPermissionInfo();
    }

    @Cacheable(value = CacheConstant.PERMISSION_KEY_PREFIX, key = "'code:'+#p0", unless = "#result == null")
    @Override
    public List<MenuInfo> getMenuPermissions(String code) {
        List<MenuInfo> menuInfoList = new ArrayList<>();
        QueryWrapper<Permission> wrapper = new QueryWrapper<>();
        Permission permission = new Permission();
        permission.setPermissionCode(code);
        wrapper.setEntity(permission);
        wrapper.orderByAsc("create_time");
        permission = this.getOne(wrapper);
        Permission con = new Permission();
        con.setParentIds(permission.getParentIds() + "/" + permission.getId());
        con.setAvailable(1);
        wrapper.setEntity(con);
        String[] resourceTypes = {MenuInfo.ResourceType.DIRECTORY.getCode(), MenuInfo.ResourceType.MENU.getCode()};
        wrapper.in("resource_type", resourceTypes);
        List<Permission> list = this.list(wrapper);
        list.remove(this.getZoneAdminPermission());
        for (Permission ps : list) {
            MenuInfo info = new MenuInfo();
            info.setOnlyId(ps.getId());
            info.setTitle(ps.getPermissionName());
            info.setHref(ps.getUrl());
            if (ps.getResourceType().equals(MenuInfo.ResourceType.DIRECTORY.getCode())) {
                con = new Permission();
                con.setParentIds(ps.getParentIds() + "/" + ps.getId());
                con.setAvailable(1);
                wrapper.setEntity(con);
                List<Permission> list2 = this.list(wrapper);
                List<MenuInfo> subMenuInfoList = new ArrayList<>();
                for (Permission subPs : list2) {
                    MenuInfo subInfo = new MenuInfo();
                    subInfo.setOnlyId(subPs.getId());
                    subInfo.setTitle(subPs.getPermissionName());
                    subInfo.setHref(subPs.getUrl());
                    subMenuInfoList.add(subInfo);
                }
                info.setChildren(subMenuInfoList);
            }
            menuInfoList.add(info);
        }
        return menuInfoList;
    }

    @Cacheable(value = CacheConstant.PERMISSION_KEY_PREFIX, key = "#root.method.name")
    @Override
    public List<Permission> getTopDirectoryPermissions() {
        Permission permission = new Permission();
        permission.setResourceType(MenuInfo.ResourceType.TOP_DIRECTORY.getCode());
        permission.setAvailable(1);
        return this.list(new QueryWrapper<>(permission));
    }


    @Override
    @Cacheable(value = CacheConstant.PERMISSION_KEY_PREFIX, key = "'getRolemissionMenu:role:'+#role.id", unless = "#result == null")
    public List<PermissionInfo> getCurrentUserPermissionMenu(Role role) {
        Subject subject = SecurityUtils.getSubject();
        Permission superPer = this.getOne(new QueryWrapper<Permission>().eq("permission_code", "*"));

        List<PermissionInfo> permissionInfos = this.allPermissionInfo();
        if (!role.getPermissionIds().contains("," + superPer.getId() + ",")) {
            List<PermissionInfo> permissionInfoList = new ArrayList<>();
            for (PermissionInfo permissionInfo : permissionInfos) {
                Permission p = this.getById(permissionInfo.getValue());
                if (subject.isPermitted(p.getPermissionCode())) {
                    List<PermissionInfo> permissionInfoList1 = new ArrayList<>();
                    for (PermissionInfo permissionInfo1 : permissionInfo.getData()) {
                        Permission p1 = this.getById(permissionInfo1.getValue());
                        if (subject.isPermitted(p1.getPermissionCode())) {
                            List<PermissionInfo> permissionInfoList2 = new ArrayList<>();
                            for (PermissionInfo permissionInfo2 : permissionInfo1.getData()) {
                                Permission p2 = this.getById(permissionInfo2.getValue());
                                if (subject.isPermitted(p2.getPermissionCode())) {
                                    permissionInfoList2.add(permissionInfo2);
                                }
                            }
                            permissionInfo1.setData(permissionInfoList2);
                            permissionInfoList1.add(permissionInfo1);
                        }
                    }
                    permissionInfo.setData(permissionInfoList1);
                    permissionInfoList.add(permissionInfo);
                }
            }
            permissionInfos = permissionInfoList;
        }
        return permissionInfos;
    }

    @Cacheable(value = CacheConstant.PERMISSION_KEY_PREFIX, key = "#root.method.name")
    public Permission getSuperAdminPermission() {
        return this.getOne(new QueryWrapper<Permission>().eq("permission_code", "*"));
    }

    @Cacheable(value = CacheConstant.PERMISSION_KEY_PREFIX, key = "#root.method.name")
    public Permission getZoneAdminPermission() {
        return this.getOne(new QueryWrapper<Permission>().eq("permission_code", "zone:*"));
    }

}